package micropascalcomp;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import micropascalcomp.FSA.*;

/**
 *
 * @author Jason Welch, Codi Gann, Jamal Martin
 */
public class CompScanner {
    String filePath;                //String that contains file path from args[]
    FileReader readFile;            //Filereader to be opened onto the file path
    LineNumberReader inFile;        //Scanner object that supports line number
    FSABehavior fsa;                //currently set FSA
    int curLine, curColumn;
    
    //FSA Behavior Definitions
    PeriodFSA period = new PeriodFSA();
    CommaFSA comma = new CommaFSA();
    SColonFSA scolon = new SColonFSA();
    LParenFSA lParen = new LParenFSA();
    RParenFSA rParen = new RParenFSA();
    EqualFSA equal = new EqualFSA();
    PlusFSA plus = new PlusFSA();
    MinusFSA minus = new MinusFSA();
    TimesFSA times = new TimesFSA();
    ColonFSA colon = new ColonFSA();
    GThanFSA gthan = new GThanFSA();
    LThanFSA lthan = new LThanFSA();
    DigitFSA digit = new DigitFSA();
    IdentifierFSA identifier = new IdentifierFSA();
    StringsFSA string = new StringsFSA(); 
    CommentsFSA comments = new CommentsFSA(); 
    
    /**
     * Constructor for the scanner.  It will open the source file, place a scanner
     * on the file, and then execute the dispatcher.
     * @param filePath File path name taken from args[0]
     */
    public CompScanner(String filePath){
        this.filePath = filePath;   //Set file path
        curColumn = 1;              //Start Column # at 1
        
        try {
            //System.out.println(filePath);   //testing only
            readFile = new FileReader(filePath);        //Establish file reader
            inFile = new LineNumberReader(readFile);    //Establish scanner on file
            inFile.setLineNumber(1);                    //Sets line numbers to 1 base
            curLine = inFile.getLineNumber();           //Set line number
        } catch (FileNotFoundException e) {
            System.out.println(e);
            System.out.println("Input file not found, Quiting");
            System.exit(1);
        }
                
    }
    
    /**
     * The Dispatcher method that moves the pointer and checks which FSA needs to run.
     * Also handles white space with a null return.  If an invalid character is found
     * it returns a token with MP_ERROR as its type and the lexeme matches the invalid
     * character.
     * @return MP_Token A container class with the token info
     */
    public MP_Token dispatcher(){
        int read;
        MP_Token found = null;
        
        try {
            if(inFile.ready()){         //make sure file has not reached the end
                inFile.mark(curColumn); //set the current location for the pointer
                read = inFile.read();   //get the next char as a ascii code
                
                if(read >= 48 && read <= 57){
                    read = 48;  //if its a number set it to one ascii value
                }
                if((read >= 65 && read <= 90) || (read >= 97 && read <= 122)){
                    read = 65;  //if its a letter set it to one ascii value
                }
                
                switch(read){           //find what FSA to perform, or clear white space
                    case 9:     // tab
                    case 32:    // space
                        //System.out.println("tab/space " + curLine + " " + curColumn);
                    	curColumn++;        //increase column counter
                        break;
                    case 10:    // '\n'
                    case 13:    // '\r'
                        //System.out.println("endline " + curLine + " " + curColumn);
                        curColumn = 1;      //reset line number
                        curLine = inFile.getLineNumber(); //Set line number
                        break;
                    case 48:    // 0-9
                    	inFile.reset();
                        setFSA(digit);     //Call ID FSA
                        found = runFSA(readFile, inFile, curColumn);
                        //System.out.println(found.getTokenByName()+" "+found.getLexeme()+" " + curLine + " " + curColumn);
                        curColumn = curColumn + found.getLexeme().length();
                        break;
                    case 95:    // "_" underscore
                    case 65:    // a-z && A-Z
                    	//Added by Codi 2-1-2011
                    	inFile.reset();
                        setFSA(identifier);     //Call ID FSA
                        found = runFSA(readFile, inFile, curColumn);
                        //System.out.println(found.getTokenByName()+" \""+found.getLexeme()+"\" " + found.getLineNum() + " " + found.getColumnNum());
                        curColumn = curColumn + found.getLexeme().length();
                        break;
                    case 39:    // "'"  Start of a string
                        inFile.reset();
                        setFSA(string);     //Call String FSA
                        found = runFSA(readFile, inFile, curColumn);
                        //System.out.println(found.getTokenByName()+" "+found.getLexeme()+" " + curLine + " " + curColumn);
                        curColumn = curColumn + found.getLexeme().length();
                        break;                        
                    case 46:    // "."  Period
                        inFile.reset();             //place pointer back to token begining
                        setFSA(period);             //Set Semi-colon FSA
                        found = runFSA(readFile, inFile, curColumn);   //run the FSA
                        curColumn = curColumn + found.getLexeme().length(); //adjust column position
                        //System.out.println(found.getLexeme() + " " + found.getLineNum() + " " + found.getColumnNum());
                        break;                        
                    case 44:    // ","  Comma
                        inFile.reset();             //place pointer back to token begining
                        setFSA(comma);              //Set Semi-colon FSA
                        found = runFSA(readFile, inFile, curColumn);   //run the FSA
                        curColumn = curColumn + found.getLexeme().length(); //adjust column position
                        //System.out.println(found.getLexeme() + " " + found.getLineNum() + " " + found.getColumnNum());
                        break;                        
                    case 59:    // ";"  Semi-colon
                        inFile.reset();             //place pointer back to token begining
                        setFSA(scolon);             //Set Semi-colon FSA
                        found = runFSA(readFile, inFile, curColumn);   //run the FSA
                        curColumn = curColumn + found.getLexeme().length(); //adjust column position
                        //System.out.println(found.getLexeme() + " " + found.getLineNum() + " " + found.getColumnNum());
                        break;                        
                    case 40:    // "("  left paren
                        inFile.reset();             //place pointer back to token begining
                        setFSA(lParen);             //Set Semi-colon FSA
                        found = runFSA(readFile, inFile, curColumn);   //run the FSA
                        curColumn = curColumn + found.getLexeme().length(); //adjust column position
                        //System.out.println(found.getLexeme() + " " + found.getLineNum() + " " + found.getColumnNum());
                        break;                        
                    case 41:    // ")"  right paren
                        inFile.reset();             //place pointer back to token begining
                        setFSA(rParen);             //Set Semi-colon FSA
                        found = runFSA(readFile, inFile, curColumn);   //run the FSA
                        curColumn = curColumn + found.getLexeme().length(); //adjust column position
                        //System.out.println(found.getLexeme() + " " + found.getLineNum() + " " + found.getColumnNum());
                        break;                        
                    case 61:    // "="  equals
                        inFile.reset();             //place pointer back to token begining
                        setFSA(equal);             //Set Semi-colon FSA
                        found = runFSA(readFile, inFile, curColumn);   //run the FSA
                        curColumn = curColumn + found.getLexeme().length(); //adjust column position
                        //System.out.println(found.getLexeme() + " " + found.getLineNum() + " " + found.getColumnNum());
                        break;                        
                    case 62:    // ">" and ">="  greater thans
                        inFile.reset();             //place pointer back to token begining
                        setFSA(gthan);             //Set Semi-colon FSA
                        found = runFSA(readFile, inFile, curColumn);   //run the FSA
                        curColumn = curColumn + found.getLexeme().length(); //adjust column position
                        //System.out.println(found.getTokenByName()+" "+found.getLexeme() + " " + found.getLineNum() + " " + found.getColumnNum());
                        break;                        
                    case 60:    // "<" "<=" and "<>" 
                        inFile.reset();             //place pointer back to token begining
                        setFSA(lthan);             //Set Semi-colon FSA
                        found = runFSA(readFile, inFile, curColumn);   //run the FSA
                        curColumn = curColumn + found.getLexeme().length(); //adjust column position
                        //System.out.println(found.getTokenByName()+" "+found.getLexeme() + " " + found.getLineNum() + " " + found.getColumnNum());
                        break;                        
                    case 58:    // ":" and ":-" colons
                        inFile.reset();             //place pointer back to token begining
                        setFSA(colon);             //Set Semi-colon FSA
                        found = runFSA(readFile, inFile, curColumn);   //run the FSA
                        //System.out.println(found.getTokenByName()+" "+found.getLexeme() + " " + found.getLineNum() + " " + found.getColumnNum());
                        curColumn = curColumn + found.getLexeme().length(); //adjust column position
                        break;                        
                    case 43:    // "+"  addition opperand
                        inFile.reset();             //place pointer back to token begining
                        setFSA(plus);             //Set Semi-colon FSA
                        found = runFSA(readFile, inFile, curColumn);   //run the FSA
                        curColumn = curColumn + found.getLexeme().length(); //adjust column position
                        //System.out.println(found.getLexeme() + " " + found.getLineNum() + " " + found.getColumnNum());
                        break;                        
                    case 45:    // "-"  minus sign
                        inFile.reset();             //place pointer back to token begining
                        setFSA(minus);             //Set Semi-colon FSA
                        found = runFSA(readFile, inFile, curColumn);   //run the FSA
                        curColumn = curColumn + found.getLexeme().length(); //adjust column position
                        //System.out.println(found.getLexeme() + " " + found.getLineNum() + " " + found.getColumnNum());
                        break;                        
                    case 42:    // "*"  times sign
                        inFile.reset();             //place pointer back to token begining
                        setFSA(times);             //Set Semi-colon FSA
                        found = runFSA(readFile, inFile, curColumn);   //run the FSA
                        curColumn = curColumn + found.getLexeme().length(); //adjust column position
                        //System.out.println(found.getLexeme() + " " + found.getLineNum() + " " + found.getColumnNum());
                        break;                        
                    case 123:    // "{"  Start of a coment block
                        inFile.reset();
                        setFSA(comments);     //Call Comment FSA
                        MP_Token tmp  = runFSA(readFile, inFile, curColumn);
                        curColumn = curColumn + tmp.getLexeme().length();
                        //System.out.println(tmp.getTokenByName()+" "+tmp.getLexeme() + " " + tmp.getLineNum() + " " + tmp.getColumnNum());
                        break;                        
                    default:
                        curColumn++;        //non-legal symble found, move past
                        found = new MP_Token(Token.MP_ERROR, "" + (char)read, curLine, curColumn);
                        System.out.println("Scanner Error on line: " + found.getLineNum()
                            + " column: " + found.getColumnNum() + " Non-legal symbol found.");
                        //System.out.println("MP_ERROR " + (char)read + " " + curLine + " " + curColumn);
                }
            } else {
                //end of file reached, return eof token
                found = new MP_Token(Token.MP_EOF, "EOF", curLine + 1, 1);
            }
        } catch (IOException e) {
            System.out.println(e);
        }
        return found;
    }
    
    /*
     * Get for higher level components to see if the scanner has reached the 
     * end of the file.
     */
    public boolean isEOF(){
        boolean result = false;
        
        try {
            result = inFile.ready();
        } catch (IOException e){
            System.out.println(e);
        }
        return result;
    }
    
    /*
     * Method to change which FSA to set as the current runnable
     */
    private void setFSA(FSABehavior toSet){
        fsa = toSet;
    }
    
    /**
     * Method that will actually execute the FSA to find a give token
     * @param file The active file reader
     * @param infile The active file scanner
     * @return MP_Token :: The new token found
     */
    private MP_Token runFSA(FileReader file, LineNumberReader infile, int colNum){
        return fsa.getToken(file, infile, colNum);
    }
    
}
